<?php namespace App\Http\Services\Wechat;

use App\Exceptions\WechatException;
use Requests_Exception;

class WechatLogin
{
    private $wechat_base_url         = 'https://open.weixin.qq.com/connect/oauth2/authorize';
    private $wechat_access_token_url = 'https://api.weixin.qq.com/sns/oauth2/access_token';
    private $wechat_userinfo_url     = 'https://api.weixin.qq.com/sns/userinfo';
    private $wechat_token_url        = 'https://api.weixin.qq.com/cgi-bin/token';
    private $wechat_qrcode_url       = 'https://api.weixin.qq.com/cgi-bin/qrcode/create';
    private $wechat_ticket_url       = 'https://api.weixin.qq.com/cgi-bin/ticket/getticket';
    private $scope = 'snsapi_userinfo';
    private $response_type = 'code';
    private $access_token;
    protected $openid;
    protected $unionid;
    protected $nickname;
    protected $sex;
    protected $province;
    protected $city;
    protected $country;
    protected $headimgurl;
    protected $ticket;

    /**
     * @param $openid
     */
    public function setOpenId($openid){
        $this->openid = $openid;
    }

    /**
     * @return mixed
     */
    public function getOpenId(){
        return $this->openid;
    }

    /**
     * @param $unionid
     */
    public function setUnionid($unionid){
        $this->unionid = $unionid;
    }

    /**
     * @return mixed
     */
    public function getUnionid(){
        return $this->unionid;
    }

    /**
     * @return mixed
     */
    public function getNickname()
    {
        return $this->nickname;
    }

    /**
     * @param mixed $nickname
     */
    public function setNickname($nickname)
    {
        $this->nickname = $nickname;
    }

    /**
     * @return mixed
     */
    public function getSex()
    {
        return $this->sex;
    }

    /**
     * @param mixed $sex
     */
    public function setSex($sex)
    {
        $this->sex = $sex;
    }

    /**
     * @return mixed
     */
    public function getProvince()
    {
        return $this->province;
    }

    /**
     * @param mixed $province
     */
    public function setProvince($province)
    {
        $this->province = $province;
    }

    /**
     * @return mixed
     */
    public function getCity()
    {
        return $this->city;
    }

    /**
     * @param mixed $city
     */
    public function setCity($city)
    {
        $this->city = $city;
    }

    /**
     * @return mixed
     */
    public function getCountry()
    {
        return $this->country;
    }

    /**
     * @param mixed $country
     */
    public function setCountry($country)
    {
        $this->country = $country;
    }

    /**
     * @return mixed
     */
    public function getHeadimgurl()
    {
        return $this->headimgurl;
    }

    /**
     * @param mixed $headimgurl
     */
    public function setHeadimgurl($headimgurl)
    {
        $this->headimgurl = $headimgurl;
    }

    /**
     * @return mixed
     */
    public function getAccessToken()
    {
        return $this->access_token;
    }

    /**
     * @param mixed $access_token
     */
    public function setAccessToken($access_token)
    {
        $this->access_token = $access_token;
    }

    /**
     * @param null $callback
     * @param string $state
     * @return string
     */
    public static function buildRedirectUrl($callback = null,$state = ''){
        $config = new config();
        $wechatLogin = new WechatLogin();
        $appid  = $config->getAppid();
        $scope  = $wechatLogin->scope;
        $response_type = $wechatLogin->response_type;

        $getCodeUrl = $wechatLogin->wechat_base_url.'?'.'appid='.$appid.'&redirect_uri='.$callback.'&response_type='.$response_type.'&scope='.$scope.'&state='.$state.'#wechat_redirect';

        return $getCodeUrl;
    }

    /**
     * @param null $code
     * @param string $state
     * @throws WechatException
     */
    public static function getWechatAccessTokenByCode($code = null,$state = ''){
        $config = new config();
        $secret = $config->getSecret();
        $appid  = $config->getAppid();

        $wechatLogin = new WechatLogin();

        if(is_null($code))
            throw new WechatException(WechatException::CodeIsNull);

        $requestUrl = $wechatLogin->wechat_access_token_url.'?appid='.$appid.'&secret='.$secret.'&code='.$code.'&grant_type=authorization_code';

        try{
            $result = \Requests::post($requestUrl,array(),array());
        }catch (Requests_Exception $ex){
            throw new WechatException(WechatException::WeChatSignInFail,['message' => $ex->getMessage()]);
        }

        if(!$result->success)
            throw new WechatException(WechatException::RequestIsFail);

        $resultInfo = json_decode($result->body);
        if($resultInfo->access_token) {
            $openid = $resultInfo->openid;
            $wechatLogin->setOpenId($openid);
            $wechatLogin->setAccessToken($resultInfo->access_token);
            return $wechatLogin;
        }else{
            throw new WechatException(WechatException::GetAccessTokenFail);
        }
    }

    /**
     * @return WechatLogin
     * @throws WechatException
     */
    public static function getWechatAccessTokenBySecret(){
        $config = new config();
        $secret = $config->getSecret();
        $appid  = $config->getAppid();

        $wechatLogin = new WechatLogin();

        $requestUrl = $wechatLogin->wechat_token_url.'?grant_type=client_credential&appid='.$appid.'&secret='.$secret;

        try{
            $result = \Requests::get($requestUrl,array(),array());
        }catch (Requests_Exception $ex){
            throw new WechatException(WechatException::WeChatSignInFail,['message' => $ex->getMessage()]);
        }

        if(!$result->success)
            throw new WechatException(WechatException::RequestIsFail);

        $resultInfo = json_decode($result->body);
        if($resultInfo->access_token) {
            $wechatLogin->setAccessToken($resultInfo->access_token);
            return $wechatLogin;
        }else{
            throw new WechatException(WechatException::GetAccessTokenFail);
        }
    }

    /**
     * @param null $code
     * @param string $state
     * @return WechatLogin
     * @throws WechatException
     */
    public static function getWechatUserInfo($code = null,$access_token=null,$state = ''){

        /** @var WechatLogin $wechatInfo */
        $wechatInfo = self::getWechatAccessTokenByCode($code,$state);

        $requestUrl = $wechatInfo->wechat_userinfo_url.'?access_token='.$wechatInfo->getAccessToken().'&openid='.$wechatInfo->getOpenId().'&lang=zh_CN';

        try{
            $result = \Requests::post($requestUrl,array(),array());
        }catch (Requests_Exception $ex){
            throw new WechatException(WechatException::WeChatSignInFail,['message' => $ex->getMessage()]);
        }

        if(!$result->success)
            throw new WechatException(WechatException::RequestIsFail);

        $resultInfo = json_decode($result->body);
        if($resultInfo->openid){
            $wechatInfo->setNickname($resultInfo->nickname);
            $wechatInfo->setSex($resultInfo->sex);
            $wechatInfo->setProvince($resultInfo->province);
            $wechatInfo->setCity($resultInfo->city);
            $wechatInfo->setCountry($resultInfo->country);
            $wechatInfo->setHeadimgurl($resultInfo->headimgurl);
            property_exists($resultInfo,'unionid') ? $wechatInfo->setUnionid($resultInfo->unionid) : $wechatInfo->setUnionid(null);

            return $wechatInfo;
        }else{
            throw new WechatException(WechatException::GetOpenIdFail);
        }

    }

    public static function getQrcode($code){
        /** @var WechatLogin $wechatInfo */
        $wechatInfo = self::getWechatAccessTokenBySecret();
        $requestUrl = $wechatInfo->wechat_qrcode_url.'?access_token='.$wechatInfo->getAccessToken();

        $param = array(
            "action_name" => "QR_LIMIT_STR_SCENE",
            "action_info" => array(
                "scene" => array(
                    "scene_str" => $code
                )
            )
        );

        try{
            $result = \Requests::post($requestUrl,array(),json_encode($param));
        }catch (Requests_Exception $ex){
            throw new WechatException(WechatException::WeChatSignInFail,['message' => $ex->getMessage()]);
        }

        if(!$result->success)
            throw new WechatException(WechatException::RequestIsFail);

        $resultInfo = json_decode($result->body);
        if(property_exists($resultInfo,'ticket'))
            return $resultInfo->ticket;
        else
            throw new WechatException(WechatException::TicketIsNull);

    }

    public static function getTicketByAccessToken(){
        $wechatInfo = self::getWechatAccessTokenBySecret();
        $requestUrl = $wechatInfo->wechat_ticket_url.'?access_token='.$wechatInfo->getAccessToken().'&type=jsapi';

        try{
            $result = \Requests::get($requestUrl,array(),array());
        }catch (Requests_Exception $ex){
            throw new WechatException(WechatException::WeChatSignInFail,['message' => $ex->getMessage()]);
        }

        if(!$result->success)
            throw new WechatException(WechatException::RequestIsFail);

        $resultInfo = json_decode($result->body);
        if($resultInfo->errmsg == 'ok') {
            $wechatInfo->setTicket($resultInfo->ticket);
            return $wechatInfo;
        }else{
            throw new WechatException(WechatException::GetAccessTokenFail);
        }
    }

    public static function createNonceStr($length = 16) {
        $chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
        $str = "";
        for ($i = 0; $i < $length; $i++) {
            $str .= substr($chars, mt_rand(0, strlen($chars) - 1), 1);
        }
        return $str;
    }

    /**
     * @return mixed
     */
    public function getTicket()
    {
        return $this->ticket;
    }

    /**
     * @param mixed $ticket
     */
    public function setTicket($ticket)
    {
        $this->ticket = $ticket;
    }
}